/*
 * EventDispatcher.h
 *
 *  Created on: 2008/12/25
 *      Author: Yongshik Cho
 */

#ifndef EVENTDISPATCHER_H_
#define EVENTDISPATCHER_H_

#include <map>

#ifndef ISLOT_H_
#	include "core/ISlot.h"
#endif

#ifndef ISIGNAL_V1_H_
#	include "core/ISignal_v1.h"
#endif

#ifndef PREREQUISITES_H_
#	include "core/Prerequisites.h"
#endif

#ifndef EXCEPTION_H_
#	include "Exception.h"
#endif

#ifndef EVENT_H_
#	include "event/Event.h"
#endif


class RenderObjectContainer;

struct ICallbackPointer
{
	virtual ~ICallbackPointer() {}
	virtual const bool isExists(const ICallbackPointer& icp) = 0;
};

template<class PARAM1>
class FunctionPointer : public ICallbackPointer
{
public:
	typedef void(*Callback)(PARAM1);

	FunctionPointer(Callback cb) : callback(cb) {}
	virtual ~FunctionPointer() { }

	virtual const bool isExists(const ICallbackPointer& icp)
	{
		FunctionPointer<PARAM1>& fnp = (FunctionPointer<PARAM1>&)icp;
		if(fnp.callback == callback)
			return true;

		return false;
	}

private:
	Callback callback;
};

template<class CallbackClass, class PARAM1>
class CallbackPointer : public ICallbackPointer
{
public:
	typedef void (CallbackClass::*Callback)(PARAM1);

	CallbackPointer(CallbackClass* cbs, Callback cb) : cb_class(cbs), callback(cb) {}
	virtual ~CallbackPointer() { }

	virtual const bool isExists(const ICallbackPointer& icp)
	{
		CallbackPointer<CallbackClass, PARAM1>& cbp = (CallbackPointer<CallbackClass, PARAM1>&)icp;
		if(cbp.cb_class == cb_class && cbp.callback == callback)
			return true;

		return false;
	}

private:
	CallbackClass* cb_class;
	void (CallbackClass::*callback)(PARAM1);
};


class EventDispatcher
{
public:
	friend class RenderObjectContainer;

	EventDispatcher();
	virtual ~EventDispatcher();

public:
	template <class CallbackClass>
	void addEventListener(const uint32 type, CallbackClass* cb_class, void (CallbackClass::*callback)(const Event&))
	{
		/*
		* if there is no same callback, let's do register callback
		*/
		if(isThereSameEvent<CallbackClass>(type, cb_class, callback))
			return;


		//register callback
		ISignal_v1<const Event&>* sig = new ISignal_v1<const Event&>;
		if(!sig)
			AltoThrow(Exception::ERR_MEMORY);

		ISlot* slot = new ISlot(sig->Connect(cb_class, callback));
		if(!slot)
			AltoThrow(Exception::ERR_MEMORY);

		Event* _e = new Event(type);
		if(!_e)
			AltoThrow(Exception::ERR_MEMORY);

		ICallbackPointer* cbp = new CallbackPointer<CallbackClass, const Event& >(cb_class, callback);
		if(!cbp)
			AltoThrow(Exception::ERR_MEMORY);

		CallbackType* cbt = new CallbackType(_e, cbp);
		if(!cbt)
			AltoThrow(Exception::ERR_MEMORY);

		mListeners[cbt] = new SignalSlotType(slot, sig);
		if(!mListeners[cbt])
			AltoThrow(Exception::ERR_MEMORY);	
	}

	void addEventListener(const uint32 type, void (*callback)(const Event&))
	{
		/*
		* if there is no same callback, let's do register callback
		*/
		if(isThereSameEvent(type, callback))
			return;

		//register callback
		ISignal_v1<const Event&>* sig = new ISignal_v1<const Event&>;
		if(!sig)
			AltoThrow(Exception::ERR_MEMORY);

		ISlot* slot = new ISlot(sig->Connect(callback));
		if(!slot)
			AltoThrow(Exception::ERR_MEMORY);

		Event* _e = new Event(type);
		if(!_e)
			AltoThrow(Exception::ERR_MEMORY);

		ICallbackPointer* cbp = new FunctionPointer<const Event& >(callback);
		if(!cbp)
			AltoThrow(Exception::ERR_MEMORY);

		CallbackType* cbt = new CallbackType(_e, cbp);
		if(!cbt)
			AltoThrow(Exception::ERR_MEMORY);

		mListeners[cbt] = new SignalSlotType(slot, sig);
		if(!mListeners[cbt])
			AltoThrow(Exception::ERR_MEMORY);
	}

	/*
	 * subEventListener
	 */
	template <class CallbackClass>
	void subEventListener(const uint32 type, CallbackClass* cb_class, void (CallbackClass::*callback)(const Event&))
	{
		// TODO Auto-generated destructor stub
		for(ListenerType::iterator it=mListeners.begin();it!=mListeners.end();)
		{
			CallbackType* cbt = (*it).first;
			if(cbt)
			{
				Event* _e = cbt->first;
				ICallbackPointer* cbp = cbt->second;
				if(_e && _e->getType() == type && cbp->isExists(CallbackPointer<CallbackClass, const Event& >(cb_class, callback)))
				{
					SignalSlotType* sst = (*it).second;
					if(sst)
					{
						SAFE_DELETE(sst->first);
						SAFE_DELETE(sst->second);
					}

					SAFE_DELETE(cbp);
					SAFE_DELETE(_e);
					SAFE_DELETE(cbt);
					SAFE_DELETE(sst);

					mListeners.erase(it);
				}
				else ++it;
			}
		}
	}
	void subEventListener(const uint32 type, void (*callback)(const Event&))
	{
		for(ListenerType::iterator it=mListeners.begin();it!=mListeners.end();)
		{
			CallbackType* cbt = (*it).first;
			if(cbt)
			{
				Event* _e = cbt->first;
				ICallbackPointer* cbp = cbt->second;
				if(_e && _e->getType() == type && cbp->isExists(FunctionPointer<const Event& >(callback)))
				{
					SignalSlotType* sst = (*it).second;
					if(sst)
					{
						SAFE_DELETE(sst->first);
						SAFE_DELETE(sst->second);
					}

					SAFE_DELETE(cbp);
					SAFE_DELETE(_e);
					SAFE_DELETE(cbt);
					SAFE_DELETE(sst);

					mListeners.erase(it);
				}
				else ++it;
			}
		}
	}

	inline const uint32 getListenerCount() const { return (uint32)mListeners.size(); }

	/*
	 * dispatch
	 */
	virtual void dispatch(const Event& e);

private:
	template<class CallbackClass>
	const bool isThereSameEvent(const uint32 type, CallbackClass* cb_class, void (CallbackClass::*callback)(const Event&))
	{
		for(ListenerType::iterator it=mListeners.begin();it!=mListeners.end();++it)
		{
			CallbackType* cbt = (*it).first;
			if(cbt)
			{
				Event* _e = cbt->first;
				ICallbackPointer* cbp = cbt->second;
				if(_e && type == _e->getType() && cbp && cbp->isExists(CallbackPointer<CallbackClass, const Event& >(cb_class, callback)))
				{
					return true;
				}
			}
		}

		return false;
	}
	const bool isThereSameEvent(const uint32 type, void (*callback)(const Event&))
	{
		for(ListenerType::iterator it=mListeners.begin();it!=mListeners.end();++it)
		{
			CallbackType* cbt = (*it).first;
			if(cbt)
			{
				Event* _e = cbt->first;
				ICallbackPointer* cbp = cbt->second;
				if(_e && type == _e->getType() && cbp && cbp->isExists(FunctionPointer<const Event& >(callback)))
				{
					return true;
				}
			}
		}

		return false;
	}

public:
	typedef pair<ISlot*, ISignal_v1<const Event&>* > SignalSlotType;
	typedef pair<Event*, ICallbackPointer* > CallbackType;
	typedef map<CallbackType*, SignalSlotType*> ListenerType;

	ListenerType mListeners;
};


#endif /* EVENTDISPATCHER_H_ */
